package aggregate

import (
	"errors"
	"gluenet/modules/manager/server/domain/dependency"
	"gluenet/modules/manager/server/domain/entity"
	"gluenet/modules/manager/server/domain/vo"
	"gluenet/modules/manager/server/infra"
	"gluenet/modules/manager/server/infra/prepare"
	"gluenet/modules/manager/server/infra/template"
	"path/filepath"
)

func init() {
	//fmt.Printf("init config factory\n")
	prepare.RegisterFactory(func() {
		intanceFactory := NewAgentFactory()
		prepare.InjectDependency(func() *AgentFactory {
			return intanceFactory
		})
	})
}

type AgentFactory struct {
	AgentRepo  dependency.AgentRepo
	DetailRepo dependency.DetailRepo
	AreaRepo   dependency.AreaRepo
	GuidCli    dependency.GuidCLI
}

func NewAgentFactory() *AgentFactory {
	factory := &AgentFactory{}
	prepare.FetchDependency(&factory.GuidCli)
	prepare.FetchDependency(&factory.AgentRepo)
	prepare.FetchDependency(&factory.DetailRepo)
	prepare.FetchDependency(&factory.AreaRepo)
	return factory
}

func (r *AgentFactory) UnRegister(guid string) error {
	obj, err := r.AgentRepo.Get(guid)
	if err != nil {
		return err
	}
	if err = r.DetailRepo.DelDetailKV(obj.ConfigRepoKey); err != nil {
		return err
	}
	if err = r.DetailRepo.DelDetailKV(obj.RunTimeRepoKey); err != nil {
		return err
	}
	if err = r.DetailRepo.DelDetailKV(obj.DetailRepoKey); err != nil {
		return err
	}
	if _, err = r.AgentRepo.Del(guid); err != nil {
		return err
	}
	return nil
}

func (r *AgentFactory) Register(req vo.AgentRegisterReq) (*entity.Agent, error) {
	rOrder := &entity.RegisterOrder{}
	rOrder.NewFormAgent(&req)

	guid, err := r.GuidCli.Register(rOrder)
	if err != nil {
		return nil, err
	}

	agent, err := r.AgentRepo.Get(guid)
	if err != nil && !errors.Is(err, infra.DataNotFind) {
		return nil, err
	}

	if errors.Is(err, infra.DataNotFind) {
		obj := &entity.Agent{}
		obj.NewFrom(&req, guid)

		area, err := r.AreaRepo.Get(req.AreaID)
		if err != nil {
			return nil, err
		}
		prefix := area.BuildAgentRepoKey(obj.GUID)
		obj.DetailRepoKey = filepath.ToSlash(filepath.Join(prefix, "detail"))
		obj.ConfigRepoKey = filepath.ToSlash(filepath.Join(prefix, "config"))
		obj.RunTimeRepoKey = filepath.ToSlash(filepath.Join(prefix, "runtime"))

		obj.DetailRepoValue = req.DetailInfo
		obj.ConfigRepoValue = template.AgentConfigTemplate(req.Role, req.Platform)
		obj.RunTimeRepoValue = map[string]interface{}{}
		if err = r.DetailRepo.PutDetailKV(obj.DetailRepoKey, obj.DetailRepoValue); err != nil {
			return nil, err
		}
		if err = r.DetailRepo.PutDetailKV(obj.ConfigRepoKey, obj.ConfigRepoValue); err != nil {
			return nil, err
		}
		if err = r.DetailRepo.PutDetailKV(obj.RunTimeRepoKey, obj.RunTimeRepoValue); err != nil {
			return nil, err
		}
		agNew, err := r.AgentRepo.Add(obj)
		if err != nil {
			return nil, err
		}
		return agNew, nil
	} else {
		agent.DetailRepoValue, err = r.DetailRepo.GetByRepoKey(agent.DetailRepoKey)
		if err != nil {
			return nil, err
		}
		agent.ConfigRepoValue, err = r.DetailRepo.GetByRepoKey(agent.ConfigRepoKey)
		if err != nil {
			return nil, err
		}
		agent.RunTimeRepoValue, err = r.DetailRepo.GetByRepoKey(agent.RunTimeRepoKey)
		if err != nil {
			return nil, err
		}

		agentNew, err := r.AgentRepo.Update(agent)
		if err != nil {
			return nil, err
		}
		return agentNew, nil
	}
}

func (r *AgentFactory) GetConfig(guid string) (interface{}, error) {
	agent, err := r.AgentRepo.Get(guid)
	if err != nil {
		return nil, err
	}
	return r.DetailRepo.GetByRepoKey(agent.ConfigRepoKey)
}

func (r *AgentFactory) PutConfig(req vo.AgentPutConfigReq) error {
	agent, err := r.AgentRepo.Get(req.Guid)
	if err != nil {
		return err
	}
	return r.DetailRepo.PutDetailKV(agent.ConfigRepoKey, req.ConfigInfo)
}

func (r *AgentFactory) QueryAgentAll() ([]entity.Agent, error) {
	objs, err := r.AgentRepo.FindAll()
	if err != nil {
		return nil, err
	}
	for k, obj := range objs {
		objs[k].ConfigRepoValue, err = r.DetailRepo.GetByRepoKey(obj.ConfigRepoKey)
		if err != nil {
			return nil, err
		}
		objs[k].DetailRepoValue, err = r.DetailRepo.GetByRepoKey(obj.DetailRepoKey)
		if err != nil {
			return nil, err
		}
		objs[k].RunTimeRepoValue, err = r.DetailRepo.GetByRepoKey(obj.RunTimeRepoKey)
		if err != nil {
			return nil, err
		}
	}
	return objs, nil
}

func (r *AgentFactory) QueryAgent(query map[string]interface{}) ([]entity.Agent, error) {
	objs, err := r.AgentRepo.FindBy(query)
	if err != nil {
		return nil, err
	}
	for k, obj := range objs {
		objs[k].ConfigRepoValue, err = r.DetailRepo.GetByRepoKey(obj.ConfigRepoKey)
		if err != nil {
			return nil, err
		}
		objs[k].DetailRepoValue, err = r.DetailRepo.GetByRepoKey(obj.DetailRepoKey)
		if err != nil {
			return nil, err
		}
		objs[k].RunTimeRepoValue, err = r.DetailRepo.GetByRepoKey(obj.RunTimeRepoKey)
		if err != nil {
			return nil, err
		}
	}
	return objs, nil
}

func (r *AgentFactory) QueryArea(id string) (*entity.Area, error) {
	obj, err := r.AreaRepo.Get(id)
	if err != nil {
		return nil, err
	}
	return obj, nil
}
